/*
服务端 -- 服务提供者、服务发现者的实现
    1. 服务提供者 -- 像服务端发送服务注册的请求
    2. 服务发现者 -- 进行服务发现，返回能够提供服务的服务端ip+端口
                    当服务上线/下线请求到来时，做出处理
*/

#pragma once

#include "Requestor.hpp"

namespace RpcModule
{
    namespace ClientModule
    {
        // 服务提供者
        class Provider
        {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr &requestor) : _requestor(requestor) {}
            // 服务提供者 -- 发送服务注册的请求
            // conn向注册中心发送请求，请求注册方法method，能提供该方法的主机为host
            bool RegistryMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host)
            {
                // 1. 发送服务注册的请求
                auto message = MessageFactory::CreateMessage<ServiceRequest>();
                message->SetId(UUID::GetUuid());
                message->SetMType(MType::REQ_SERVICE);
                message->SetOptype(ServiceOptype::SERVICE_REGISTRY);
                message->SetMethod(method);
                message->SetHost(host);
                BaseMessage::ptr result;
                bool ret = _requestor->Send(conn, message, result);
                if (ret == false)
                {
                    LOGERROR("%s 服务注册失败", method.c_str());
                    return false;
                }
                auto response = std::dynamic_pointer_cast<ServiceResponse>(result);
                if (response.get() == nullptr)
                {
                    LOGERROR("向下转换类型失败");
                    return false;
                }
                if (response->GetRcode() != RCode::RCODE_OK)
                {
                    LOGERROR("服务注册失败，原因为：%s", ErrReason(response->GetRcode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        // 对提供服务的服务端进行管理，实现RR轮转
        class MethodHost
        {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost() : _idx(0) {}
            MethodHost(const std::vector<Address> &hosts)
                : _hosts(hosts.begin(), hosts.end()),
                  _idx(0) {}
            // 服务上线，那么就需要添加新的主机
            void AppendHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }
            // 服务下线，需要删除下线的主机
            void RemoveHost(const Address &host)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++)
                {
                    if (*it == host)
                    {
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            // 根据RR轮转，选出应该处理服务的服务端并返回
            Address ChooseHost()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = _idx++ % _hosts.size();
                return _hosts[pos];
            }
            bool Empty()
            {
                std::unique_lock<std::mutex> lock(_mutex);
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            size_t _idx;
            std::vector<Address> _hosts;
        };
        // 服务发现者
        class Discoverer
        {
        public:
            using ptr = std::shared_ptr<Discoverer>;
            using OfflineCallBack = std::function<void(const Address &)>;
            Discoverer(const Requestor::ptr &requestor, const OfflineCallBack &callback)
                : _requestor(requestor), _offline_callback(callback) {}
            // 进行服务发现 -- 发送服务发现请求，处理应答，找到提供服务的服务端，连接服务端，进行服务处理
            bool ServiceDiscovery(const BaseConnection::ptr &conn, const std::string &method, Address &address)
            {
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    // 1. 如果一个服务在本地有Host能够提供服务，那么就不用进行服务发现
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end())
                    {
                        if (it->second->Empty() == false)
                        {
                            // 说明数组中保存有Host能够提供服务，不需要服务发现
                            address = it->second->ChooseHost();
                            return true;
                        }
                    }
                }
                // 2. 发送服务发现请求，进行服务发现
                ServiceRequest::ptr request = MessageFactory::CreateMessage<ServiceRequest>();
                request->SetId(UUID::GetUuid());
                request->SetMType(MType::REQ_SERVICE);
                request->SetMethod(method);
                request->SetOptype(ServiceOptype::SERVICE_DISCOVERY);
                BaseMessage::ptr response;
                bool ret = _requestor->Send(conn, request, response);
                if (ret == false)
                {
                    LOGERROR("服务发现失败");
                    return false;
                }
                // 3. 收到响应之后，需要判断响应中是否有能够提供服务的主机
                ServiceResponse::ptr realresponse = std::dynamic_pointer_cast<ServiceResponse>(response);
                if (!realresponse)
                {
                    LOGERROR("向下转换失败");
                    return false;
                }
                if (realresponse->GetRcode() != RCode::RCODE_OK)
                {
                    LOGERROR("服务发现%s失败, %s", method.c_str(), ErrReason(realresponse->GetRcode()).c_str());
                    return false;
                }
                std::vector<Address> hosts = realresponse->GetHosts();
                LOGDEBUG("hosts.empty() = %d", hosts.empty());
                MethodHost::ptr methodhost = std::make_shared<MethodHost>(hosts);
                if (methodhost->Empty() == true)
                {
                    // 说明没有任何一个可以提供服务的主机
                    LOGERROR("请求方法%s失败，因为没有提供该服务的主机", method.c_str());
                    return false;
                }
                {
                    // 走到这里，说明服务发现有能提供该服务的主机
                    // 需要将它加入到_method_hosts中，并返回主机ip和端口
                    std::unique_lock<std::mutex> lock(_mutex);
                    _method_hosts[method] = methodhost;
                    address = methodhost->ChooseHost();
                }
                return true;
            }
            // 提供给Dispatcher模块，当服务端进行服务上线/下线通知时，要对通知进行处理
            void OnServiceRequest(const BaseConnection::ptr &conn, const ServiceRequest::ptr &message)
            {
                // 针对服务端发送来的服务上线/服务下线请求的处理
                ServiceOptype optype = message->GetOptype();
                std::string method = message->GetMethod();
                if (optype == ServiceOptype::SERVICE_ONLINE)
                {
                    // 1. 服务上线请求
                    // 有新服务上线，需要插入到MethodHost中
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                    {
                        MethodHost::ptr methodhost = std::make_shared<MethodHost>();
                        methodhost->AppendHost(message->GetHost());
                    }
                    else
                    {
                        it->second->AppendHost(message->GetHost());
                    }
                }
                else if (optype == ServiceOptype::SERVICE_OFFLINE)
                {
                    // 2. 服务下线请求
                    // 有服务下线，需要从_method_hosts中删除一个host
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end())
                        return;
                    else
                    {
                        it->second->RemoveHost(message->GetHost());
                        // 对于长连接，当服务下线时，需要删除长连接
                        _offline_callback(message->GetHost());
                    }
                }
                else
                {
                    // 3. 其它未知的，非法请求
                    LOGERROR("收到来自服务端的非法请求");
                    return;
                }
            }

        private:
            OfflineCallBack _offline_callback;
            std::mutex _mutex;
            // 一个服务可以由哪些服务端提供
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts;
            Requestor::ptr _requestor;
        };
    }
}